/* 
    四、手写实现MyPromise源码
    要求：尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理。
*/

const { resolve } = require("../../../a_web课程资料/fed-e-003/codes/01-01-03-01-my-promise/myPromise");
const { reject } = require("lodash");

// promise的三种状态
const PADDING = "padding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

// 声明一个promie类
class MyPromise {
  // 构造函数
  constructor(executor) {
    try {
      // 执行函数
      executor(this.resolve, this.reject);
    } catch (e) {
      // 抛出错误
      this.reject(e);
    }
  }
  // 全局变量
  status = PENDING;
  // 成功之后的值
  value = undefined;
  // 失败原因
  reason = undefined;
  // 成功回调数组
  successCallback = [];
  // 失败回调数组
  failCallback = [];

  // 箭头函数将this指向promise对象
  resolve(value) {
    // padding状态才能继续执行，
    if (this.status !== PENDING) return;
    // padding --> fulfilled
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    while (this.successCallback.length) this.successCallback.shift()();
  }
  reject(reason) {
    // padding状态才能执行
    if (this.status !== PADDING) return;
    // padding --> REJECTED
    this.status = REJECTED;
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 删除该回调并执行
    while (this.failCallback.length) this.failCallback.shift()();
  }
  then(successCallback, failCallback) {
    // 两个回调函数的默认值
    successCallback = successCallback ? successCallback : (value) => value;
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        };
    // then方法返回一个新的promise函数，用于链式调用
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          //  创建微任务
          try {
            let x = successCallback(this.value);
            // 判断x是普通值还是promise对象
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status == REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }

  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback().then(() => value));
      },
      (reason) => {
        return MyPromise.resolve(
          callback().then(() => {
            throw reason;
          })
        );
      }
    );
  }
  /* 
  本就是一个回调函数
  好处是可以接受所有的promise传递过来的错误
  可以被链式调用
*/
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  static all(array) {
      let result = [];
      let index = 0;
      return new MyPromise((resolve,reject) => {
          function addData(key,value) {
              result[key] = value;
              index ++;
              if(index === array.length) {
                  resolve(result)
              }
          }
          for (let i = 0;i < array.length ;i++) {
              let current = array[i]
              if (current instanceof MyPromise) {
                  current.then(value => addData(i,value),reason => reject(reason))
              }else{
                  addData(i,array[i])
              }
          }
      })
  }

  static resolve(value) {
      if (value instanceof MyPromise) return value;
      return new MyPromise(resolve => resolve(value);)
  }
}

function resolvePromise(promise2,x,resolve,reject){
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve,reject)
    }else{
        resolve(x)
    }
}